perm filename UDPSER.MAC[IP,SYS] blob sn#746425 filedate 1984-03-21 generic text, type T, neo UTF8
	title	UDPSer
	subttl	provan

	search	f,s
	search	NetDef		; network definitions
	search	MacTen		; search only if symbol not found in NetDef

	sall

	$reloc
	$high

XP	VUDPSr,1		; UDP version
comment	\

this module contains the support routines for the User Datagram
protocol as defined in RFC-768.

\
	subttl	defintions describing a UDP leader

; see RFC-768 for details of this header.


UDPLen==:2		; number of words in an UDP leader.

	$low		; define the storage needed

UDPIBH:	block	NBHLen			; buffer header.
UDPIBf:	block	UDPLen			; words needed for header

; the following block is used to create a UDP leader for output.
;  it is filled and then converted to 36 bit buffers all under ScnOff.
UDPObf:	block	NBHLen+UDPLen		; output buffer for forming leader

	$high		; back to protected code

UDPPnt:	point	8,UDPIBf		; pointer to start loading the
					;  header block from the stream.

; define the actual header fields.  position is the bit position of the
;  left most bit.
;
; 	name   word  position width

; UDP uses the standard ports, StdSP and StdDP.
;DefFd.	UDPSP,	0,	0,	16	; source port of message
;DefFd.	UDPDP,	0,	16,	16	; destination port
DefFd.	UDPSiz,	1,	0,	16	; length of packet
DefFd.	UDPChk,	1,	16,	16	; UDP checksum
	subttl	definitions

	subttl	process incoming UDP message


entry	UDPIn	; only load this module if IP calls this routine


UDPIn::
	move	p2,MsgLen(f)		; get length of message through IP
ifn FtChck,<	; doing checksum
	setz	p3,			; clear checksum
	move	t1,p2			; make sure to checksum length
					;  of UDP message before we
					;  convert it to length of segment.
	pushj	p,CSmHWd##		; checksum the length.
>
	caige	p2,UDPLen*4		; cut length by that amount
	  jrst	NoLead			; not enough message to read in leader
	movei	t1,UDPIBH		; get pointer to input leader
	move	t2,ABfLst(f)		; get last buffer so far
	stor.	t1,NBHNxt,(t2)		; make us their next
	movem	t1,ABfLst(f)		; and make us last (for grins)
	move	t1,UDPPnt		; point at the storage block
	movei	t2,UDPLen*4		; length of leader in bytes
	stor.	t2,NBHCnt,UDPIBH	; store in buffer header
	pushj	p,GetLed##		; get the leader and checksum
	  jrst	NoLead			; not enough bytes for leader.

	load.	t1,UDPSiz,UDPIBf	; get length from our packet
	camle	t1,p2			; IP at least as big as we need?
	  jrst	NoMess			; nope.  forget it.
	subi	t1,UDPLen*4		; cut length by amount of leader
	movem	t1,MsgLen(f)		; save length of UDP message
	pushj	p,GetMes##		; copy T1 bytes in.
	  jrst	NoMess			; problem reading message

	move	p1,t1			; save new stream pointer for later.

ifn FtChck,<	; doing checksumming
	load.	t1,UDPChk,UDPIBf	; get the checksum from the leader
	jumpe	t1,UDPNCk		; this guy doesn't do checksums

	move	t1,RmtAdr(f)		; get their address.
	pushj	p,CSmWrd##		; add in that checksum.
	move	t1,LclAdr(f)		; our address
	pushj	p,CSmWrd##		; checksum it.
	move	t1,Protcl(f)		; get the protocol
	pushj	p,CSmHWd##		; checksum that half a word
	

	; bear in mind that the checksum we now have in P3 has, along with
	;  all the right stuff, its own one's complement.  therefore, what
	;  we really have is <checksum> + -<checksum>, which is 0.
	;  further, since <checksum> has some bit on (otherwise the
	;  sender isn't checksuming and we wouldn't be here), it can be
	;  shown that the brand of one's complement 0 we must have is
	;  the version with all 1's.  if that's what we have, we're ok.
	;  if not, the checksum failed.
	hrrzs	p3			; get just the checksum
	caie	p3,<1←↑d16>-1		; magic explained above
	  jrst	BadChk			; checksum is bad.

UDPNCk:	; here to skip over the checksum checks because sender is not
	;  checksumming the messages.
>
	aos	UDPMsg##		; count another UDP message seen

	move	t1,RmtAdr(f)		; source (foreign host address)
	load.	t2,StdSP,TCPIBf		; get his port
	movem	t2,RmtPrt(f)		; and keep pseudo DDB up-to-date
	load.	t3,StdDP,TCPIBf		; get my port
	movem	t3,LclPrt(f)		; still keep pseudo DDB up-to-date
	move	t4,Protcl(f)		; get protocol
	move	p3,MsgLen(f)		; put length of this message
					;  somewhere where we can get
					;  it for the new DDB.
	push	p,f			; save current DDB, in case we fail
	pushj	p,FndDDB##		; scan network DDBs for the one
					;  that matches.
	  jrst	NewCon			; this is one we haven't heard of
	pop	p,(p)			; don't want that F any more.

NewLst:					; return here if we are now listening
					;  for an unknown port (exec port).
	movem	p3,MsgLen(f)		; remember the message length
					;  in the new DDB.

	pass packet to owner
	subttl	process a connection which has no DDB


; handle a connection to a port which is not listening.
; port number is in T3.  old DDB (at this writing, always the pseudo
;	DDB) is on the stack.  it STAYS on the stack through most of
;	this routine, so watch your ass or you'll try to popj p, to it.
NewCon:
	; remember that we STILL have the old DDB on the stack.

	; first check for a perpetual listen on that socket
	movei	t4,PlsLen-1		; point at last entry
NewCo1:	camn	t3,PlsPrt(t4)		; is this it?
	  jrst	PLsSn			; yes.  a perptual listen seen.
	sojge	t4,NewCo1		; count down

	caxl	t3,FrePrt		; is it below freely assigned ports?
	  jrst	NotExc			; yes.  not an exec port.

	; now check for pemanent port services, handled through Telnet
	skipe	t1,t3			; position our port number better
					; (zero isn't legal)
	 PUSHJ	P,WKPFND	;IS THIS SOCKET'S SERVICE IMPLEMENTED?
	  jrst	NoPort			; remember this "error"
	move	t4,t1			; save service offset
	MOVEI	J,0		;NO JOB NUMBER YET
	PUSHJ	P,DDBGET##	;TRY FOR FREE DDB
	  jrst	NoDDB			; can't get one
	PUSHJ	P,ITYGET##	;GET A PORT
	  jrst	NoITY			; can't get one
	MOVSI	u,TTYKBD!TTYPTR
	IORb	u,TTYLIN(F)	; SET TTY BITS, get ITY's LDB into U
	PUSHJ	P,TSETBI##	;CLEAR INPUT BUFFER
	PUSHJ	P,TSETBO##	;CLEAR OUTPUT BUFFER
	move	t1,t4			; position pointer to service.
	HRRO	T2,WKPSRV(T1)	;FETCH POINTER TO LOGICAL NAME
	POP	T2,DEVLOG(F)	;SET LOGICAL NAME INTO DDB
	LDB	T1,WKPTFC	;FETCH TTY FORCED COMMAND INDEX
	pushj	p,TTFORC##		;FORCE THE APPROPRIATE COMMAND

; here from perpetual listen setup
NowCon:	pushj	p,PrpDDB		; set essential DDB words

	pop	p,t2			; get back the DDB which was used
					;  while the message was arriving.

	;now fill in the information we know
	move	t1,RmtAdr(t2)		; get the foreign host address.
	movem	t1,RmtAdr(f)		; and save it the real DDB
	move	t1,NetAdr(t2)		; get ARPA address
	movem	t1,NetAdr(f)		; save in the DDB
	move	t1,RmtPrt(t2)		; get the source port (his port)
	movem	t1,RmtPrt(f)		; save in DDB
	move	t1,LclPrt(t2)		; get the destination port (my port)
	movem	t1,LclPrt(f)		; save in DDB
	movei	t1,S%List		; get state code "listen"
	movem	t1,State(f)		; make it this DDB's state

	pushj	p,NewLst		; go back a process this message
					;  as if nothing has happened.
	move	t2,State(f)		; now get the state
	caie	t2,S%List		; still listening?
	  popj	p,			; no.  just return.
	pushj	p,DDBFls##		; clear out DDB
	pjrst	DDBRel##		; and return it to free pool

; here to deal with a perpetual listen found
PLsSn:	move	j,PlsJob(t4)		; get job number listening
	pushj	p,DDBGet##		; get a DDB and assign it to this job.
	  jrst	NoDDB			; can't.  count and deny access
	movei	t1,PlsPID(t4)		; point at the PID to notify
	hrrzi	t2,DevNam(f)		; point at the device name in the
					; DDB as the data to send.
	hrli	t2,1			; just that one word, please.
	setz	j,			; mark as being sent from interupt
					;  level.
	pushj	p,SendSI##		; send the IPCF packet to the user
	  jrst	NoIPCF			; oops.  flush DDB and deny connection
	jrst	NowCon			; now process this packet


NotExc:	pop	p,f			; restore fake DDB.
	movei	u,TCPIBf		; point at TCP leader
	move	p3,TCPFlg(u)		; get the flags from leader.
	jumpe	p2,TryRst		; just reset if no options
	hlrz	t1,p2			; get the first buffer of options
	pushj	p,RelBuf##		; free the options.
	jrst	TryRst			; try to send a reset and
					;  return the buffers and return.
;ROUTINE TO CHECK LEGALITY OF AN EXEC Well Known Port.
;	MOVE	t1,[local port NUMBER]
;	PUSHJ	P,WKPFND
;	  ERROR--SERVICE NOT IMPLEMENTED
;	NORMAL--T1 CONTAINS INDEX INTO SERVER TABLE (WKPSRV)

WKPFND:	pushj	p,save2##		; get p1 and p2
	move	p2,t1			; save port number
	MOVSI	t1,-WKPNUM	;NUMBER OF SERVICES IMPLEMENTED
WKPFN1:	LDB	p1,WKPSKT	;FETCH SOCKET NUMBER OF THIS SERVICE
	CAMN	p1,p2		;MATCH?
	  JRST	CPOPJ1		;YES, GOOD RETURN, T1 is offset.
	AOBJN	t1,WKPFN1	;NO, TRY NEXT
	POPJ	P,		;ERROR--SERVICE NOT IMPLEMENTED


;TABLE OF DEFINED SERVICES AVAILABLE THROUGH EXEC WKP.
;   MACRO TO DEFINE A SERVICE:
;	SERVER	(PORT# , TTY FORCED COMMAND , LOGICAL NAME)


DEFINE SERVER(SKT,TFC,NAME) <
	↑D<SKT>B26 + TFC## ,, [SIXBIT\NAME\]
>

WKPSRV:
;[tcp]	SERVER	(3,TTFCXF,FTPSRV)	;FILE TRANSFER PROTOCOL SERVER
	SERVER	(21,TTFCXF,FTPSRV)	;[tcp] FILE TRANSFER PROTOCOL SERVER
	SERVER	(23,TTFCXH,NETUSR)	;TELNET SERVER
	server	(79,ttfcxg,FngSrv)	;(241) finger service
IFN FTPATT,<
	0		;SPACE TO PATCH IN NEW SERVICES
	0
>
	WKPNUM==.-WKPSRV	;NUMBER OF DEFINED SERVICES

WKPSKT:	POINT	9,WKPSRV(T1),8	;POINTER TO SERVICE SOCKET NUMBER
WKPTFC:	POINT	9,WKPSRV(T1),17	;POINTER TO TTY FORCED COMMAND INDEX
	subttl	returns

; message ended before leader was read in
NoLead:	aos	UDELed##		; error with leader
	popj	p,			; return

; bytes ended before message or ran out of buffers while reading it
NoMess:	aos	UDEMes##		; count error reading message in
	popj	p,

BadChk:	aos	UDEChk##		; checksum wrong.  count it
	pjrst	BufFls			; flush out buffers and return

NoPort:	aosa	TCEPrt##		; incoming to a exec port we
					;  don't watch.
NoDDB:	  aos	TCEDDB##		; couldn't get DDB when needed.
BadCon:	pop	p,f			; restore fake DDB with info in it.
	scnoff				; stop interupts
	pushj	p,SndNSP##		; call ICMP to tell him we
					;  don't do that.
	scnon				; interrupts ok again.
	jrst	BufFls			; go flush message and options

NoIPCF:	aosa	TCEIPC##		; IPCF failed
NoITY:	  aos	TCEITY##		; couldn't get an ITY when i
					;  wanted one.
	pushj	p,DDBREL##		; RETURN THE DDB
	jrst	BadCon			; do bad connection things


; subroutine to release all the buffers in our message.
BufFls:	hlrz	t1,p1			; get first buffer of chain.
	pjrst	RelBuf##		; release the entire chain.
v	subttl	UDPMak

;++
; Functional description:
;
;	put UDP leader (in 32 bit format) into fixed UDP output leader
;	buffer.  then link the buffer to the beginning of the
;	current output stream.  then send the message down to the
;	next level of protocol for further processing.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,UDPMak
;		<always returns here>
;
; Input parameters:
;
;	f - DDB for connection
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	data in DDB
v;
; Implicit outputs:
;
;	data in DDB
;
; Routine value:
;
;	returns non-skip if can't get a buffer
;
; Side effects:
;
;	adds a buffer to the beginning of the current output stream.
;--


UDPMak::
	movei	t1,UDPOBf		; point at the output leader space
	exch	t1,OBfFst(f)		; make us first, get old first
	stor.	t1,NBHNxt,UDPOBf	; link old first to us.
	move	t1,RmtPrt(f)		; get his port
	stor.	t1,StdDP,NBHLen+UDPOBf	; that's the destination port
	move	t1,LclPrt(f)		; get my port
	stor.	t1,StdSP,NBHLen+UDPOBf	; that's the source port
	movei	t1,UDPLen*4		; get length in bytes
	stor.	t1,NBHCnt,UDPOBf	; save byte count for this buffer
	addb	t1,OBfByt(f)		; get a grand total in bytes.
	stor.	t1,UDPSiz,NBHLen+UDPOBf	; save in length word.

	; one would add OPTIONS around here somewhere.

ifn FtChck,<	; doing checksums?
	move	t1,[point 16,NBHLen+UDPOBf]; starting pointer
	movei	t2,UDPLen*4		; get length in bytes of leader
	pushj	p,CSmWds##		; and checksum it.
	move	t1,RmtAdr(f)		; get remote address
	pushj	p,CSmWrd##		; add it to checksum
	move	t1,LclAdr(f)		; local address, too
	pushj	p,CSmWrd##		; add it in.
	move	t1,Protcl(f)		; and get protocol
	pushj	p,CSmHWd##		; and add it in as well
	move	t1,OBfByt(f)		; get byte count of message
					;  plus leader
	pushj	p,CSmHWd##		; add that to checksum, too.

	txc	p3,msk.hw		; send one's complement of the sum
	txnn	p3,msk.hw		; if zero, make it...
	  movei	p3,msk.hw		; ...the zero with all bits on
	stor.	p3,UDPChk,NBHLen+UDPOBf	; save the checksum in the leader.
>
ife FtChck,<	; not doing checksums
	zero.	t1,UDPChk,NBHLen+UDPOBf	; flag that we aren't checksumming
>
	pjrst	IpMake##		; call next level of protocol
	subttl	UDPChk

;++
; Functional description:
;
;	subroutine to do various once a second checks to an IMP DDB.
;
;
; Calling sequence:
;
;		move	f,DDB
;		pushj	p,UDPChk##
;		<always returns here>
;
; Input parameters:
;
;	f - DDB of an IMP device.
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	DDB and queues
;
; Implicit outputs:
;
;	DDB and queues
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	may didle with output queues if it finds it needs to retransmit.
;	may delete DDB altogether, although DevSer will still have the
;	link to the next DDB.  (HINT: call this after doing everything else.)
;--


UDPChk::
	should need this
	subttl	TcpRst

;++
; Functional description:
;
;	subroutine to do various things for a job that just did
;	a RESET UUO.
;
;
; Calling sequence:
;
;		move	j,<job number>
;		pushj	p,TCPRst
;		<always returns here>
;
; Input parameters:
;
;	j - job number reseting
;
; Output parameters:
;
;	none.
;
; Implicit inputs:
;
;	perpetual listen tables.
;
; Implicit outputs:
;
;	perpetual listen tables.
;
; Routine value:
;
;	none.
;
; Side effects:
;
;	will clear out the PID for any entry set last by this job.
;--


TCPRst::
	movei	t1,PlsLen-1		; point at last entry in tables

TCPRs1:	camn	j,PlsJob(t1)		; is this me?
	  setzm	PlsPID(t1)		; yes.  clear it by clearing the PID
	sojge	t1,TCPRs1		; try the next.

	popj	p,			; all done.

	$high
	$LIT
	END